import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class BaseWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._normalSize = QSize(1600, 900)
        self._startPos = QPoint()
        self._endPos = QPoint()
        self._resizeEdgePadding = 10
        self._resizing = None
        self._dragging = False

        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        self.resize(self._normalSize)
        self.setMouseTracking(True)

        self.win_head = QWidget()
        self.win_head.setObjectName('win_head')
        self.win_head.setMouseTracking(True)
        self.win_head.setFixedHeight(36)
        self.initWindowHead(self.win_head)

        self.win_main = QWidget()
        self.win_main.setObjectName('win_pages')
        self.win_main.setMouseTracking(True)

        vlayout = QVBoxLayout(self)
        vlayout.addWidget(self.win_head, 0)
        vlayout.addWidget(self.win_main, 1)
        vlayout.setSpacing(0)
        vlayout.setContentsMargins(0, 0, 0, 0)

        with open('demo.css', encoding='utf-8') as qss:
            self.setStyleSheet(qss.read())

    def initWindowHead(self, win):
        label_head_title = QLabel()
        label_head_title.setText('小林通 - 基础窗口')

        button_head_min = QPushButton()
        button_head_min.setObjectName('button_head_min')
        button_head_min.clicked.connect(self.onWindowMin)

        button_head_max = QPushButton()
        button_head_max.setObjectName('button_head_max')
        button_head_max.clicked.connect(self.onWindowMax)

        button_head_close = QPushButton()
        button_head_close.setObjectName('button_head_close')
        button_head_close.clicked.connect(self.onWindowClose)

        layout = QHBoxLayout(win)
        layout.addWidget(label_head_title)
        layout.addStretch(0)
        layout.addWidget(button_head_min)
        layout.addWidget(button_head_max)
        layout.addWidget(button_head_close)
        layout.setSpacing(9)
        layout.setContentsMargins(18, 0, 18, 0)

    def onWindowMin(self):
        self.showMinimized()

    def onWindowMax(self):
        if self.isMaximized():
            self.showNormal()
        else:
            self.showMaximized()

    def onWindowClose(self):
        self.close()

    def mousePressEvent(self, event: QMouseEvent):
        self._startPos = event.pos()
        if event.button() == Qt.MouseButton.LeftButton:
            self._resizing = self.checkResizeEdges(event.pos())
            if self.win_head.rect().contains(event.pos()):
                self._dragging = True

    def mouseReleaseEvent(self, event: QMouseEvent):
        self._dragging = False
        self._resizing = None

    def mouseMoveEvent(self, event: QMouseEvent):
        if self._resizing:
            self.resizeWindow(event.pos())
        elif self._dragging:
            self.move(self.pos() + (event.pos() - self._startPos))
        else:
            self.updateCursorIcon(event.pos())

    def resizeWindow(self, mousePos):
        if not self._resizing:
            return
        currentRect = self.geometry()
        oldWidth = currentRect.width()
        oldHeight = currentRect.height()

        if 'right' in self._resizing:
            currentRect.setRight(currentRect.right() + (mousePos.x() - self._startPos.x()))
        if 'bottom' in self._resizing:
            currentRect.setBottom(currentRect.bottom() + (mousePos.y() - self._startPos.y()))
        if 'left' in self._resizing:
            currentRect.setLeft(currentRect.left() + (mousePos.x() - self._startPos.x()))
            if currentRect.width() < self.minimumWidth():
                currentRect.setLeft(currentRect.right() - self.minimumWidth())
        if 'top' in self._resizing:
            currentRect.setTop(currentRect.top() + (mousePos.y() - self._startPos.y()))
            if currentRect.height() < self.minimumHeight():
                currentRect.setTop(currentRect.bottom() - self.minimumHeight())

        self.setGeometry(currentRect)
        self._startPos = mousePos

        if 'left' in self._resizing:
            self._startPos.setX(self._startPos.x() + (currentRect.width() - oldWidth))
        if 'top' in self._resizing:
            self._startPos.setY(self._startPos.y() + (currentRect.height() - oldHeight))

    def checkResizeEdges(self, mousePos):
        edges = []
        if mousePos.x() < self._resizeEdgePadding:
            edges.append('left')
        if self.width() - mousePos.x() < self._resizeEdgePadding:
            edges.append('right')
        if mousePos.y() < self._resizeEdgePadding:
            edges.append('top')
        if self.height() - mousePos.y() < self._resizeEdgePadding:
            edges.append('bottom')
        return edges

    def updateCursorIcon(self, pos):
        edges = self.checkResizeEdges(pos)
        if 'top' in edges and 'left' in edges or 'bottom' in edges and 'right' in edges:
            self.setCursor(Qt.SizeFDiagCursor)
        elif 'top' in edges and 'right' in edges or 'bottom' in edges and 'left' in edges:
            self.setCursor(Qt.SizeBDiagCursor)
        elif 'left' in edges or 'right' in edges:
            self.setCursor(Qt.SizeHorCursor)
        elif 'top' in edges or 'bottom' in edges:
            self.setCursor(Qt.SizeVerCursor)
        else:
            self.setCursor(Qt.ArrowCursor)

    def mouseDoubleClickEvent(self, event: QMouseEvent):
        if event.button() == Qt.MouseButton.LeftButton:
            if self.win_head.rect().contains(event.pos()):
                self.onWindowMax()
        elif event.button() == Qt.MouseButton.MiddleButton:
            if self.win_head.rect().contains(event.pos()):
                self.resize(self._normalSize)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        opt = QStyleOption()
        opt.initFrom(self)
        self.style().drawPrimitive(QStyle.PrimitiveElement.PE_Widget, opt, painter, self)
        painter.end()
        QWidget.paintEvent(self, event)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = BaseWidget()
    win.show()
    app.exec()